#include <stdio.h>
#include "ringbuf.h"

ringbuf_t *ringbuf_create(int size) {
    ringbuf_t *r = (ringbuf_t *)malloc(sizeof(ringbuf_t));

	if (!r)
		return NULL;
	
	r->buf = (char *)malloc(size);
	
	if (r->buf) {
		memset(r->buf, 0, size);
		r->rptr = r->wptr = r->buf;
		r->size = size;
		r->rnum = r->wnum = 0;
		return r;
	}
	
	free (r);
	return NULL;
}

void ringbuf_destroy(ringbuf_t *r) {
	if (r->buf)
		free (r->buf);
	free (r);
}

bool ringbuf_empty(ringbuf_t *r) {
	if (r->wnum == r->rnum)
		return true;
	return false;
}

bool ringbuf_full(ringbuf_t *r) {
	if ( (r->wnum - r->rnum) == r->size )
		return true;
	return false;
}

int ringbuf_size(ringbuf_t *r) {
	return r->size;
}

int ringbuf_used(ringbuf_t *r) {
	return r->wnum - r->rnum;
}

int ringbuf_unused(ringbuf_t *r) {
	return r->size - (r->wnum - r->rnum);
}

bool ringbuf_push(ringbuf_t *r, char *data, int len) {
	int end;
	
	if (ringbuf_unused(r) < (len + sizeof(int)))
		return false;

	if (r->wptr >= r->rptr) {
		end = r->size - (r->wptr - r->buf);
		if ( end > (len + sizeof(int)) ) {
			memcpy(r->wptr, &len, sizeof(int));
			memcpy(r->wptr + sizeof(int), data, len);
			r->wptr += len + sizeof(int);
		} else {
			if (end < sizeof(int)) {
				memcpy(r->wptr, &len, end);
				memcpy(r->buf, &len + end, sizeof(int) - end);
				memcpy(r->buf + (sizeof(int) - end), data, len);
				r->wptr = r->buf + (len + (sizeof(int) - end));
			} else if (end > sizeof(int)) {
				memcpy(r->wptr, &len, sizeof(int));
				memcpy(r->wptr + sizeof(int), data, end - sizeof(int));
				memcpy(r->buf, data + (end - sizeof(int)), len - (end - sizeof(int)));
				r->wptr = r->buf + (len - (end - sizeof(int)));
			} else {
				memcpy(r->wptr, &len, sizeof(int));
				memcpy(r->buf, data, len);
				r->wptr = r->buf + len;
			}
		}
	} else {
		memcpy(r->wptr, &len, sizeof(int));
		memcpy(r->wptr + sizeof(int), data, len);
		r->wptr += len + sizeof(int);
	}

	r->wnum = (r->wnum + len + sizeof(int)) % (r->size + 1);

	return true;
}

char *ringbuf_pop(ringbuf_t *r, char *buf, int buf_len) {
	int used, len, *ptr, end;

	if ( ringbuf_used(r) == 0 )
		return NULL;

	used = r->wptr - r->rptr;
		
	if (used > 0) {
		ptr = (int *)r->rptr;
		len = *ptr;
		if (len > used || buf_len < len) {
			return NULL;
		}
		memcpy(buf, r->rptr, len);
		r->rptr += len + sizeof(int);
	} else if (used < 0) {
		used = r->size + used;
		end = r->size - (r->rptr - r->buf);
		if (end < sizeof(int)){
			memcpy(&len, r->rptr, end);
			memcpy(&len + end, r->buf, sizeof(int) - end);
			if (len > used || buf_len < len) {
				return NULL;
			}
			memcpy(buf, r->buf + (sizeof(int) - end), len);
			r->rptr = r->buf + (sizeof(int) - end + len);
		} else if (end > sizeof(int)) {
			memcpy(&len, r->rptr, sizeof(int));
			if (len > used || buf_len < len) {
				return NULL;
			}
			memcpy(buf, r->rptr, end - sizeof(int));
			memcpy(buf + (end - sizeof(int)), r->buf, len - (end - sizeof(int)));
			r->rptr = r->buf + len - (end - sizeof(int));
		} else {
			memcpy(&len, r->rptr, sizeof(int));
			if (len > used || buf_len < len) {
				return NULL;
			}
			memcpy(buf, r->buf, len);
			r->rptr = r->buf + len;
		}
	} else {
	}

	r->rnum = (r->rnum + len + sizeof(int)) % (r->size + 1);
	
	return buf;
}

